﻿// $Id: DomainModelCreator.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Definition of \c Salamanca.DataAccess.DomainModelCreator.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections.Generic;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Reflection;
using System.Text;

namespace Salamanca.DataAccess
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Delegate that is used to create a new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance,
    /// based on the specified data.</summary>
    /// <typeparam name="TDomainModel">The expected <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance type.</typeparam>
    /// <param name="data">The data used to create a new instance.</param>
    /// <returns>A new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</returns>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public delegate TDomainModel DomainModelCreateHandler<TDomainModel>(IDataTransferObject data)
        where TDomainModel:
            IDomainModel;



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Functor used to create new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instances.</summary>
    /// <typeparam name="TDomainModel">The expected <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance type.</typeparam>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class DomainModelCreator<TDomainModel>
        where TDomainModel:
            IDomainModel
    {

        /// <summary>Constructor.</summary>
        private DomainModelCreator()
        {
        }

        /// <summary>Constructor.</summary>
        /// <param name="handler">The delegate used to create a new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</param>
        /// <param name="list">An optional list of handlers. These must be <see cref="DomainModelCreateHandler{TDomainModel}" /> instances or <see cref="MemberInfo" /> instances.</param>
        public DomainModelCreator(DomainModelCreateHandler<TDomainModel> handler, params object[] list)
        {
            Debug.Assert(handler!=null);
            if (handler==null)
                throw new ArgumentNullException("handler");

            AddHandlersOrMembers(handler, list);
        }

        /// <summary>Constructor.</summary>
        /// <param name="member">The member used to create a new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.
        /// During its invocation, the member will be passed a <see cref="IDataTransferObject" /> instance.</param>
        /// <param name="list">An optional list of handlers. These must be <see cref="DomainModelCreateHandler{TDomainModel}" /> instances or <see cref="MemberInfo" /> instances.</param>
        public DomainModelCreator(MemberInfo member, params object[] list)
        {
            Debug.Assert(member!=null);
            if (member==null)
                throw new ArgumentNullException("member");

            AddHandlersOrMembers(GetHandler(member), list);
        }

        /// <summary>Invokes the functor with the specified data.</summary>
        /// <remarks>The functor will call all the registered handlers until one returns a non null <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</remarks>
        /// <param name="data">The data passed to the functor.</param>
        /// <returns>A new <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> instance.</returns>
        public TDomainModel Invoke(IDataTransferObject data)
        {
            TDomainModel ret=default(TDomainModel);

            foreach (DomainModelCreateHandler<TDomainModel> handler in _HandlerList)
            {
                ret=handler(data);
                if (ret!=null)
                    return ret;
            }

            return Activator.CreateInstance<TDomainModel>();
        }

        /// <summary>Adds a list of handlers.</summary>
        private void AddHandlersOrMembers(DomainModelCreateHandler<TDomainModel> first, object[] list)
        {
            if (list==null)
                list=new object[0];

            _HandlerList=new List<DomainModelCreateHandler<TDomainModel>>(list.Length+1);
            _HandlerList.Add(first);

            foreach (object item in list)
            {
                DomainModelCreateHandler<TDomainModel> h=item as DomainModelCreateHandler<TDomainModel>;
                if (h==null)
                {
                    MemberInfo mi=item as MemberInfo;
                    if (mi==null)
                    {
                        Debug.Assert(false);
                        throw new ArgumentException("list");
                    } else
                        h=GetHandler(mi);
                }

                _HandlerList.Add(h);
            }
        }

        /// <summary>Gets a <see cref="DomainModelCreateHandler{TDomainModel}" /> from the specified member.</summary>
        private static DomainModelCreateHandler<TDomainModel> GetHandler(MemberInfo member)
        {
            return new DomainModelCreateHandler<TDomainModel>(
                delegate(IDataTransferObject data) {
                    InstanceDescriptor descriptor=new InstanceDescriptor(member, new object[] { data }, true);
                    return (TDomainModel)descriptor.Invoke();
                }
            );
        }

        private List<DomainModelCreateHandler<TDomainModel>> _HandlerList;

    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Static methods to handle <see cref="DomainModelCreator{TDomainModel}" /> instances.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public static class DomainModelCreator
    {

        /// <summary>Creates a default functor (will use the <include file="../doc/Global.doc.xml" path="doc/template[@name='DomainModel']/*" /> default constructor).</summary>
        public static DomainModelCreator<TDomainModel> CreateDefault<TDomainModel>()
            where TDomainModel:
                IDomainModel,
                new()
        {
            return new DomainModelCreator<TDomainModel>(
                new DomainModelCreateHandler<TDomainModel>(
                    delegate(IDataTransferObject data) {
                        return new TDomainModel();
                    }
                )
            );
        }
    }

}
